import { Polyline } from '../polyline';
import View from 'ol/View';
import BingMaps from 'ol/source/BingMaps';
import Feature from 'ol/Feature';
import { Draw, Modify, Snap } from 'ol/interaction.js';
import { LineString, Point, Polygon, MultiPoint } from 'ol/geom';
import { defaults as defaultInteractions, Pointer as PointerInteraction } from 'ol/interaction';
import { Component, OnInit, AfterViewInit, OnDestroy } from '@angular/core';
import { fromLonLat } from 'ol/proj';
import { defaults as defaultControls, Attribution } from 'ol/control';
import { fromEvent, Subscription } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
import { TileJSON, Vector as VectorSource, OSM } from 'ol/source.js';
import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer.js';
import { Circle as CircleStyle, Fill, Icon, Stroke, Style } from 'ol/style.js';
import KML from 'ol/format/KML.js';
import { toContext } from 'ol/render.js';
import Stamen from 'ol/source/Stamen.js';
import GeoJSON from 'ol/format/GeoJSON.js';
import ExtentInteraction from 'ol/interaction/Extent.js';
import { platformModifierKeyOnly } from 'ol/events/condition.js';

/**dynamic data */
const n = 200,
    omegaTheta = 30000,
    R = 7e6,
    r = 2e6,
    p2 = 2e6;

const params = {
    handleDownEvent(evt) {
        const _map = evt.map;

        const feature = _map.forEachFeatureAtPixel(evt.pixel, f => f);

        if (feature) {
            this.coordinate_ = evt.coordinate;
            this.feature_ = feature;
        }

        return !!feature;
    },
    handleDragEvent(evt) {
        const deltaX = evt.coordinate[0] - this.coordinate_[0];
        const deltaY = evt.coordinate[1] - this.coordinate_[1];

        const geometry = this.feature_.getGeometry();
        geometry.translate(deltaX, deltaY);

        this.coordinate_[0] = evt.coordinate[0];
        this.coordinate_[1] = evt.coordinate[1];
    },
    handleMoveEvent(evt) {
        if (this.cursor_) {
            const _map = evt.map;
            const feature = _map.forEachFeatureAtPixel(evt.pixel, f => f);
            const element = _map.getTargetElement();
            if (feature) {
                // tslint:disable-next-line:triple-equals
                if (element.style.cursor != this.cursor_) {
                    this.previousCursor_ = element.style.cursor;
                    element.style.cursor = this.cursor_;
                }
            } else if (this.previousCursor_ !== undefined) {
                element.style.cursor = this.previousCursor_;
                this.previousCursor_ = undefined;
            }
        }
    },
    handleUpEvent() {
        this.coordinate_ = null;
        this.feature_ = null;
        return false;
    }
};
// tslint:disable-next-line:space-before-function-paren
const Drag = (function(pi) {
    function DragConstructor() {
        pi.call(this, params);

        /**
         * @type {module:ol/pixel~Pixel}
         * @private
         */
        this.coordinate_ = null;

        /**
         * @type {string|undefined}
         * @private
         */
        this.cursor_ = 'pointer';

        /**
         * @type {module:ol/Feature~Feature}
         * @private
         */
        this.feature_ = null;

        /**
         * @type {string|undefined}
         * @private
         */
        this.previousCursor_ = undefined;
    }
    if (pi) {
        DragConstructor['__proto__'] = pi;
    }
    DragConstructor.prototype = Object.create(pi && pi.prototype);
    DragConstructor.prototype.constructor = DragConstructor;
    return DragConstructor;
})(PointerInteraction);

@Component({
    selector: 'itmp-ol-map-static',
    templateUrl: './ol-map-static.component.html',
    styleUrls: ['./ol-map-static.component.css']
})
export class OlMapStaticComponent implements OnInit, AfterViewInit, OnDestroy {
    map: any;
    mapOptions: any;
    view: any;
    attribution: any;
    lonlat = {
        london: fromLonLat([-0.12755, 51.507222]),
        moscow: fromLonLat([37.6178, 55.7517]),
        istanbul: fromLonLat([28.9744, 41.0128]),
        rome: fromLonLat([12.5, 41.9]),
        bern: fromLonLat([7.4458, 46.95])
    };
    mapStyle = {
        width: '80%',
        height: '400px',
        margin: '0 auto'
    };

    styleSelection = 'Aerial';
    stylesOptions = [
        { value: 'Aerial', label: 'Aerial', key: 'AtRTijlPJCJAs8aIWhiEGUvMGlxnczq06e23X86tXryif4YuAAoQmzdNOhvBTtbi' },
        { value: 'AerialWithLabels', label: 'Aerial with labels', key: 'AiEL_55izn07VK2VVPxsJ6eEY8V5zI2f3udUO2UqlS1caQN3wrkDBhZXYlaIUIX4' }
    ];

    /**绘图用的变量 */
    draw: any = null;
    snap: any = null;
    source: any = null;
    drawType = 'Point';
    drawTypeOptions = ['Point', 'LineString', 'Polygon', 'Circle'];
    layers: any[] = [];

    /**style function 变量*/
    scale: number = null;
    symbol = [[0, 0], [4, 2], [6, 0], [10, 5], [6, 3], [4, 5], [0, 0]];
    styleCache = {};

    /**dynamic data */
    imageStyle;
    headInnerImageStyle;
    headOuterImageStyle;

    /**extent-interaction */
    extent;
    eventSubscriptions: Array<Subscription> = [];

    /**feature move animation */
    polyline = Polyline.join('');

    constructor() {}

    onReady(_e) {
        if (_e) {
            this.map = _e;
            // this.onChange(this.stylesOptions[0]['value']);
            // this.drawStart();
            /**dynamic data */
            // this.map.render();
            // this.map.on('postcompose', e => this.postComposeHandle(e));

            /**extend interaction */
            this.map.addInteraction(this.extent);
            this.extent.setActive(false);
        }
    }

    initMap() {
        /**extent interaction */
        this.extent = new ExtentInteraction({
            condition: platformModifierKeyOnly
        });

        /**dynamic data */
        this.imageStyle = new Style({
            image: new CircleStyle({
                radius: 5,
                fill: new Fill({ color: 'yellow' }),
                stroke: new Stroke({ color: 'red', width: 1 })
            })
        });

        this.headInnerImageStyle = new Style({
            image: new CircleStyle({
                radius: 2,
                fill: new Fill({ color: 'blue' })
            })
        });

        this.headOuterImageStyle = new Style({
            image: new CircleStyle({
                radius: 5,
                fill: new Fill({ color: 'black' })
            })
        });

        /* this.attribution = new Attribution({
            collapsible: false
        }); */
        this.view = new View({
            center: [0, 0],
            zoom: 2
        });
        /* this.layers = this.stylesOptions.map(
            (style, index) =>
                new TileLayer({
                    visible: false,
                    preload: Infinity,
                    source: new BingMaps({
                        key: style['key'],
                        imagerySet: style['value'],
                        maxZoom: 19
                    })
                })
        ); */
        const raster = new TileLayer({
            source: new OSM()
            /* source: new Stamen({
                layer: 'toner'
            }) */
        });
        this.source = new VectorSource({
            url: 'content/data/geojson/countries.geojson',
            format: new GeoJSON()
        });
        const vector = new VectorLayer({
            source: this.source
            /* source: new VectorSource({
                url: '/content/data/2012_Earthquakes_Mag5.kml',
                format: new KML({
                    extractStyles: false
                })
            }), */
            /* style: new Style({
                fill: new Fill({
                    color: 'rgba(255,255,255, 0.2)'
                }),
                stroke: new Stroke({
                    color: '#ffcc33',
                    width: 5
                }),
                image: new CircleStyle({
                    radius: 7,
                    fill: new Fill({
                        color: '#ffcc33'
                    })
                })
            }) */
            // style: (feature => {
            //     // 2012_Earthquakes_Mag5.kml stores the magnitude of each earthquake in a
            //     // standards-violating <magnitude> tag in each Placemark.  We extract it from
            //     // the Placemark's name instead.
            //     const name = feature.get('name');
            //     const magnitude = parseFloat(name.substr(2));
            //     const size = parseInt(10 + 40 * (magnitude - 5) + '', 10);
            //     this.scale = size / 10;
            //     let style = this.styleCache[size];
            //     if (!style) {
            //         const canvas = /** @type {HTMLCanvasElement} */ (document.createElement('canvas'));
            //         const vectorContext = toContext(
            // /** @type {CanvasRenderingContext2D} */(canvas.getContext('2d')),
            //             { size: [size, size], pixelRatio: 1 });
            //         vectorContext.setStyle(new Style({
            //             fill: new Fill({ color: 'rgba(255, 153, 0, 0.4)' }),
            //             stroke: new Stroke({ color: 'rgba(255, 204, 0, 0.2)', width: 2 })
            //         }));
            //         vectorContext.drawGeometry(new Polygon([this.symbol.map(coordinate => [coordinate[0] * this.scale, coordinate[1] * this.scale])]));
            //         style = new Style({
            //             image: new Icon({
            //                 img: canvas,
            //                 imgSize: [size, size],
            //                 rotation: 1.2
            //             })
            //         });
            //         this.styleCache[size] = style;
            //     }
            //     return style;
            // })
        });

        // 标记和矢量图形
        // const pointFeature = new Feature(new Point([0, 0]));
        // const lineFeature = new Feature(new LineString([[-1e7, 1e6], [-1e6, 3e6]]));
        // const polygonFeature = new Feature(
        //     new Polygon([
        //         [
        //             [-3e6, -1e6],
        //             [-3e6, 1e6],
        //             [-1e6, 1e6],
        //             [-1e6, -1e6],
        //             [-3e6, -1e6]
        //         ] /* , [[-2.5e6, -5e5], [-2.5e6, 5e5],
        //     [-5e5, 5e5], [-5e5, -5e5], [-2e6, -5e5]] */
        //     ])
        // );
        // const controls = defaultControls({ attribution: false }).extend([this.attribution]);
        // const loadTilesWhileAnimating = true;
        // const loadTilesWhileInteracting = true;
        // const interactions = defaultInteractions().extend([new Drag()]);
        const layers = [
            /* new TileLayer({
                source: new TileJSON({
                    url: 'https://api.tiles.mapbox.com/v3/mapbox.geography-class.json?secure'
                })
            }),
            new VectorLayer({
                source: new VectorSource({
                    features: [pointFeature, lineFeature, polygonFeature]
                }),
                style: new Style({
                    image: new Icon({
                        anchor: [0.5, 46],
                        anchorXUnits: 'fraction',
                        anchorYUnits: 'pixels',
                        opacity: 0.5,
                        src: 'content/images/out.png'
                    }),
                    stroke: new Stroke({
                        width: 3,
                        color: [255, 0, 0, 1]
                    }),
                    fill: new Fill({
                        color: [0, 0, 255, 0.6]
                    })
                })
            }) */
            raster,
            vector
        ];
        this.mapOptions = {
            view: this.view,
            // controls,
            // interactions,
            layers
            // loadTilesWhileAnimating,
            // loadTilesWhileInteracting
        };
    }

    rotateLeft() {
        if (this.view) {
            this.view.animate({
                rotation: this.view.getRotation() - Math.PI / 2
            });
        }
    }

    rotateRight() {
        if (this.view) {
            this.view.animate({
                rotation: this.view.getRotation() + Math.PI / 2
            });
        }
    }

    panToLon() {
        if (this.view) {
            this.view.animate({
                center: this.lonlat.london
            });
        }
    }

    elasticToMosc() {
        if (this.view) {
            this.view.animate({
                center: this.lonlat.moscow,
                duration: 2000,
                easing: elastic
            });
        }
    }

    checkSize() {
        if (this.map) {
            const small = this.map.getSize()[0] < 600;
            this.attribution.setCollapsible(small);
            this.attribution.setCollapsed(small);
        }
    }

    onChange(style: string) {
        this.stylesOptions.forEach((st, i) => this.layers[i].setVisible(st['value'] === style));
    }

    /**绘图api */
    addInteractions() {
        this.draw = new Draw({
            source: this.source,
            type: this.drawType
        });
        this.map.addInteraction(this.draw);
        this.snap = new Snap({ source: this.source });
        this.map.addInteraction(this.snap);
    }

    onTypeChange(_e) {
        this.map.removeInteraction(this.draw);
        this.map.removeInteraction(this.snap);
        this.addInteractions();
    }

    drawStart() {
        const modify = new Modify({ source: this.source });
        this.map.addInteraction(modify);
        this.onTypeChange('Point');
    }

    /**dynamic data */
    postComposeHandle(_e) {
        const vectorContext = _e.vectorContext;
        const frameState = _e.frameState;
        const theta = 2 * Math.PI * frameState.time / omegaTheta;
        const coordinates = [];
        for (let i = 0; i < n; ++i) {
            const t = theta + 2 * Math.PI * i / n;
            const x = (R + r) * Math.cos(t) + p2 * Math.cos((R + r) * t / r);
            const y = (R + r) * Math.sin(t) + p2 * Math.sin((R + r) * t / r);
            coordinates.push([x, y]);
        }

        vectorContext.setStyle(this.imageStyle);
        vectorContext.drawGeometry(new MultiPoint(coordinates));

        const headPoint = new Point(coordinates[coordinates.length - 1]);

        vectorContext.setStyle(this.headInnerImageStyle);
        vectorContext.drawGeometry(headPoint);

        vectorContext.setStyle(this.headOuterImageStyle);
        vectorContext.drawGeometry(headPoint);

        this.map.render();
    }

    ngOnInit() {
        /**attribution */
        this.eventSubscriptions[0] = fromEvent(window, 'resize')
            .pipe(debounceTime(500))
            .subscribe(_e => this.checkSize());

        /**extent interaction */
        this.eventSubscriptions[1] = fromEvent(window, 'keydown').subscribe((e: KeyboardEvent) => {
            if (e.keyCode === 16) {
                this.extent.setActive(true);
            }
        });
        this.eventSubscriptions[2] = fromEvent(window, 'keyup').subscribe((e: KeyboardEvent) => {
            if (e.keyCode === 16) {
                this.extent.setActive(false);
            }
        });
    }

    ngAfterViewInit(): void {
        setTimeout(() => this.initMap());
    }

    ngOnDestroy(): void {
        if (this.eventSubscriptions.length > 0) {
            this.eventSubscriptions.forEach(sub => sub.unsubscribe());
        }

        if (this.map) {
            this.map.off();
        }
    }
}

// A bounce easing method (from https://github.com/DmitryBaranovskiy/raphael).
function bounce(t) {
    const s = 7.5625;
    const p = 2.75;
    let l;
    if (t < 1 / p) {
        l = s * t * t;
    } else {
        if (t < 2 / p) {
            t -= 1.5 / p;
            l = s * t * t + 0.75;
        } else {
            if (t < 2.5 / p) {
                t -= 2.25 / p;
                l = s * t * t + 0.9375;
            } else {
                t -= 2.625 / p;
                l = s * t * t + 0.984375;
            }
        }
    }
    return l;
}

// An elastic easing method (from https://github.com/DmitryBaranovskiy/raphael).
function elastic(t) {
    return Math.pow(2, -10 * t) * Math.sin((t - 0.075) * (2 * Math.PI) / 0.3) + 1;
}
